package com.bsoft.ecommerce.filter;
import com.alibaba.fastjson.JSON;
import com.bsoft.ecommerce.constant.CommonConstant;
import com.bsoft.ecommerce.constant.GatewayConstant;
import com.bsoft.ecommerce.util.TokenParseUtil;
import com.bsoft.ecommerce.vo.JwtToken;
import com.bsoft.ecommerce.vo.LoginUserInfo;
import com.bsoft.ecommerce.vo.UsernameAndPassword;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicReference;
/**
 * @program: e-commerce-springclound
 * @description:
 * @author: hdh
 * @create: 2022:06:23 14:51
 */
//全局登录鉴权
@Slf4j
@Component
public class GlobalLoginOrRegisterFilter implements GlobalFilter, Ordered {

    @Autowired
    private LoadBalancerClient loadBalancerClient;
    @Autowired
    private RestTemplate restTemplate;
    //登录注册 鉴权
    // 如果是登录或者 注册则去授权中心拿到token 并返回客户端
    //
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //如果登录的话
        if (request.getURI().getPath().contains(GatewayConstant.LOGIN_URI)) {
            //去授权中心拿toekn
            String token = getTokenFromAuthorityCenter(request, GatewayConstant.AUTHORITY_CENTER_REGISTER_URL_FORMAT);
            //header  不能设置null
            response.getHeaders().add(
                    CommonConstant.JWT_USER_INFO_KEY, null == token ? "null" : token
            );
            response.setStatusCode(HttpStatus.OK);
            return response.setComplete();
        }
        //如果是注册
        if (request.getURI().getPath().contains(GatewayConstant.REGISTER_URI)) {
            //去授权中心拿token ；先创建用户 再返回token
            String token = getTokenFromAuthorityCenter(
                    request, GatewayConstant.AUTHORITY_CENTER_TOKEN_URL_FORMAT
            );
            response.getHeaders().add(CommonConstant.JWT_USER_INFO_KEY,
                    null == token ? "null" : token);
            response.setStatusCode(HttpStatus.OK);
            return response.setComplete();
        }
        //不访问其他的服务 则鉴权 。校验是否能够从token 中解析出用户信息
        HttpHeaders headers = request.getHeaders();
        String token = headers.getFirst(CommonConstant.JWT_USER_INFO_KEY);
        LoginUserInfo loginUserInfo = null;
        try {
            loginUserInfo = TokenParseUtil.parseUserInfoFromToken(token);
        } catch (Exception e) {
            log.error("parse user info  from token error [{}]", e.getMessage(), e);
        }
        // 获取不能登录用户信息 ,返回401
        if (null == loginUserInfo) {
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        //解析通过，则放行
        return chain.filter(exchange);
    }
    @Override
    public int getOrder() {
        return HIGHEST_PRECEDENCE + 2;
    }
    //从授权中心获取token
    private String getTokenFromAuthorityCenter(ServerHttpRequest request, String uriFormat) {
        //负载均衡
        ServiceInstance serviceInstance = loadBalancerClient.choose(
                CommonConstant.AUTHORITY_CENTER_SERVICEE_ID
        );
        log.info("nacos Client Info [{}]", serviceInstance.getServiceId(), serviceInstance.getInstanceId(), JSON.toJSONString(serviceInstance.getMetadata()));
        String requestUrl = String.format(uriFormat, serviceInstance.getHost(), serviceInstance.getPort());
        UsernameAndPassword usernameAndPassword = JSON.parseObject(parseBodyFromRequest(request), UsernameAndPassword.class);
        log.info("login  request URL and body", request, JSON.toJSONString(usernameAndPassword));
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        log.info("请求url"+requestUrl);
        JwtToken token = restTemplate.postForObject(requestUrl, new HttpEntity<>(JSON.toJSONString(usernameAndPassword), httpHeaders),
                JwtToken.class
        );
        if (null != token) {
            return token.getToken();
        }
        return null;
    }
     //从post 请求中获取到请求数据
    private String parseBodyFromRequest(ServerHttpRequest request) {
        // 获取请求体
        Flux<DataBuffer> body = request.getBody();
        AtomicReference<String> bodyRef = new AtomicReference<>();
        // 订阅缓冲区去消费请求体中的数据
        body.subscribe(buffer -> {
            CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
            // 一定要使用 DataBufferUtils.release 释放掉, 否则, 会出现内存泄露
            DataBufferUtils.release(buffer);
            bodyRef.set(charBuffer.toString());
        });

        // 获取 request body
        return bodyRef.get();
    }

}
